package com.rockcent.api.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.rockcent.api.Prop;
import com.rockcent.api.Routes;
import com.rockcent.api.helper.UserHelper;
import com.rockcent.api.helper.WechatPaymentHelper;
import com.rockcent.common.common.JsonResult;
import com.rockcent.common.common.RestDoing;
import com.rockcent.common.controller.BaseController;
import com.rockcent.mall.lib.domain.Mall;
import com.rockcent.mall.lib.domain.Merchant;
import com.rockcent.mall.lib.domain.MerchantRechargePayment;
import com.rockcent.mall.lib.domain.enumclass.PaymentMethod;
import com.rockcent.mall.lib.dto.UserInfoDto;
import com.rockcent.mall.lib.dto.WechatPaymentAttachDto;
import com.rockcent.mall.lib.dto.WechatRechargeDto;
import com.rockcent.mall.lib.helper.QRCodeHelper;
import com.rockcent.mall.lib.repository.MallRepository;
import com.rockcent.mall.lib.repository.MerchantRepository;
import com.rockcent.service.WechatRechargeService;
import com.thoughtworks.xstream.XStream;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback;
import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.NoTransactionException;
import org.springframework.web.bind.annotation.*;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * Created by Marco on 17-3-28.
 */
@RestController
@RequestMapping(value = Routes.API_VERSION)
public class WechatRechargeController extends BaseController {

    private final Logger log = LoggerFactory.getLogger(WechatRechargeController.class);

    @Autowired
    private WechatRechargeService wechatRechargeService;
    @Autowired
    private WechatPaymentHelper wechatPaymentHelper;
    @Autowired
    private MallRepository mallRepository;
    @Autowired
    private MerchantRepository merchantRepository;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private Prop prop;
    @Autowired
    private QRCodeHelper qrCodeHelper;
    @Autowired
    private UserHelper userHelper;

    /**
     * @api {post} /wechat/recharge 微信扫码支付
     * @apiGroup wechat
     * @apiVersion 1.0.0
     * @apiParam {BigDecimal} rechargeAmount <code>必须参数</code> 充值金额
     * @apiParam {Long} mallId <code>必须参数</code> 平台ID
     * @apiParam {Long} merchantId <code>必须参数</code> 商户ID
     *
     * @apiSuccess {String} timeStamp 时间截
     * @apiSuccess {String} codeUrl 二维码链接
     * @apiSuccess {String} package 预支付交易会话标识
     * @apiSuccess {String} paySign 签名
     * @apiSuccess {String} appId 公众账号ID
     * @apiSuccess {String} signType 签名类型
     * @apiSuccess {String} nonceStr 随机字符串
     * @apiSuccess {String} outTradeNo 充值号
     *
     * @apiSuccessExample {json} Success-Response:
     *
     * {
     *   "code": "1",
     *   "desc": "成功",
     *   "data": {
     *     "timeStamp": "1490854336",
     *     "codeUrl": "weixin://wxpay/bizpayurl?pr=w4SGFMx",
     *     "package": "prepay_id=wx201703301412160efcba8bc60314814432",
     *     "paySign": "1E64CB87A7176730B9D4B14387957E11",
     *     "appId": "wxce62f97f15d07734",
     *     "signType": "MD5",
     *     "nonceStr": "1490854336448"
     *   }
     * }
     *
     */
    @RequestMapping(value = Routes.WECHAT_RECHARGE, method = RequestMethod.POST)
    public JsonResult recharge(@RequestBody WechatRechargeDto wechatRechargeDto, HttpServletRequest request, HttpServletResponse response) {
        UserInfoDto userInfoDto = userHelper.getSession(request);
        RestDoing doing = jsonResult -> {
            BigDecimal rechargeAmount = wechatRechargeDto.getRechargeAmount();
            Long mallId = wechatRechargeDto.getMallId();
            Long merchantId = wechatRechargeDto.getMerchantId();
            if (rechargeAmount==null) {
                jsonResult.remind("充值金额不能为空",log);
                return;
            }
            if (rechargeAmount.compareTo(BigDecimal.valueOf(0))<=0) {
                jsonResult.remind("充值金额不能少于等于0",log);
                return;
            }
            if (mallId==null) {
                jsonResult.remind("平台ID不能为空",log);
                return;
            }
            if(mallId!=null && mallId.longValue()<=0) {
                jsonResult.remind("非法平台ID",log);
                return;
            }
            Mall mall = mallRepository.findById(mallId);
            if(mall==null) {
                jsonResult.remind("平台不存在",log);
                return;
            }
            if (merchantId==null) {
                jsonResult.remind("商户ID不能为空",log);
                return;
            }
            if(merchantId!=null && merchantId.longValue()<=0) {
                jsonResult.remind("非法商户ID",log);
                return;
            }
            Merchant merchant = merchantRepository.findByIdAndMallId(merchantId,mallId);
            if(merchant==null) {
                jsonResult.remind("商户不存在",log);
                return;
            }
            //生成支付记录
            MerchantRechargePayment merchantRechargePayment = wechatRechargeService.create(rechargeAmount,mallId,merchantId,userInfoDto.getId());
            WechatPaymentAttachDto wechatPaymentAttachDto = new WechatPaymentAttachDto();
            wechatPaymentAttachDto.setDomain(mall.getMobileHostName());
            wechatPaymentAttachDto.setPayId(merchantRechargePayment.getId());
            WxMpService wxMpService = wechatPaymentHelper.getWxMpService(mall.getMobileHostName());
            if(wxMpService==null) {
                jsonResult.remind("支付配置数据不存在",log);
                return;
            }
            //组装请求参数
            WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
            wxPayUnifiedOrderRequest.setAttach(objectMapper.writeValueAsString(wechatPaymentAttachDto));
            wxPayUnifiedOrderRequest.setBody("充值");
            wxPayUnifiedOrderRequest.setOutTradeNo(merchantRechargePayment.getSerialNum());
            wxPayUnifiedOrderRequest.setTotalFee(rechargeAmount.multiply(BigDecimal.valueOf(100)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue());
            wxPayUnifiedOrderRequest.setSpbillCreateIp(request.getRemoteHost());
            wxPayUnifiedOrderRequest.setTradeType(PaymentMethod.NATIVE.toString());
            wxPayUnifiedOrderRequest.setProductId(merchantRechargePayment.getSerialNum());
            wxPayUnifiedOrderRequest.setNotifyURL(prop.wechatPaymentNotifyUrl);
            //向微信发起请求
            Map<String, String> payResult = wxMpService.getPayService().getPayInfo(wxPayUnifiedOrderRequest);
//            log.info("======================>wechat payment result => {}", payResult);
            if(payResult!=null) {
                payResult.put("outTradeNo", merchantRechargePayment.getSerialNum());
            }
            jsonResult.data = payResult;
        };
        return doing.go(request, log);
    }

    /**
     * @api {post} /wechat/qrcode 生成二维码的base64字符串
     * @apiGroup wechat
     * @apiVersion 1.0.0
     *
     * @apiParam {Long} codeUrl <code>必须参数</code> 二维码链接
     *
     * @apiSuccess {String} data base64字符串
     *
     * @apiSuccessExample {json} Success-Response:
     *
     * {
     *   "code": "1",
     *   "desc": "成功",
     *   "data": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAAAGX0lEQVR42u3dW27jMBBEUWf/i062ECRidTV1LubTr9g8GkAtUJ9vSaN9fAUShBKEkiCUIJQEoQShJAglCCVBKEEoCUIJQkkQShBKglCCUBKEEoSSIJQglAShBKEkCCUIJUEoQSgJQglCSRBKEEqCUIJQEoQShJIglCCUBKEE4e/f9fM1+O9vn/CpZyX/iuTvlXzW7NqAEEIIIYQQQgghhBBCCCGEEEIIIYQQwmd/1Nn3euoxTy3Ettd56lkb1waEEEIIIYQQQgghhBBCCCGEEEIIIYQQNsA4txRmP0/yLz33d926NiCEEEIIIYQQQgghhBBCCCGEEEIIIYTwbQiTp/KTp9fbDgEQQgghhBBCCCGEEEIIIYQQQgghhBBCCGH/iOKpv7TtYuiNBxcIIYQQQgghhBBCCCGEEEIIIYQQQgghvBvhxh81ucRnt7dIDlHuWIcQQgghhBBCCCGEEEIIIYQQQgghhBBC+J9FP3vxscc0byPi1mgQegyEEEIIIYQQegyEEEIIIYQQegyEWxDe0bkNHdoGJLNjnvsXkq8AQgghhBBCCAUhhBBCCCGEghBCCJ9Y4m2Djb99wllO/d/z6oEEhBBCCCGEEEIIIYQQQgghhBBCCCGEEA6eyp/drKHtdZLfT9uWwRBCCCGEEEIIIYQQQgghhBBCCCGEEEK4l9ws1OQQZZZK//YW5w5bEEIIIYQQQgghhBBCCCGEEEIIIYQQQrgFYZJB2ysnEbZdFp/8LSCEEEIIIYQQQgghhBBCCCGEEEIIIYTwNLA7BhLJ7/Acwrbhx2W/KYQQQgghhBBCCCGEEEIIIYQQQgghhC8fUfRvv5tcUsmLodvGIW2/F4QQQgghhBBCCCGEEEIIIYQQQgghhBDmT6/3b4Vw7lLj5LNmN+RtOxxDCCGEEEIIIYQQQgghhBBCCCGEEEIIYWezW9Ce+4Rtmwj3/3tq/UAIIYQQQgghhBBCCCGEEEIIIYQQQghhJ6fkcjn3o86S678wfXa0ACGEEEIIIYQQQgghhBBCCCGEEEIIIYR7hw3nhgRtm1DccTu36zlBCCGEEEIIIYQQQgghhBBCCCGEEEII4eAYI3nqvP/mZG2Xs8+OlIwoIIQQQgghhBBCCCGEEEIIIYQQQggh7BxRtMFouxy5/93v+FYhhBBCCCGEEEIIIYQQQgghhBBCCCGEsGH80L9ZQ3Ip9H+e5BJvG3VACCGEEEIIIYQQQgghhBBCCCGEEEIIYcMYY/YwMTsAaBs/JH/l2U2EIYQQQgghhBBCCCGEEEIIIYQQQgghhLABYf+/c0thltO5RX/9BsEQQgghhBBCCCGEEEIIIYQQQgghhBC+HGH/hcX9p+DbBhKzzzKigBBCCCGEEEIIIYQQQgghhBBCCCGE8G6EyW1z2zZraPuEbUvc5r8QQgghhBBCCCGEEEIIIYQQQgghhBDehDD57m03Obv1AJTk5AJuCCGEEEIIIYQQQgghhBBCCCGEEEIIGxD2bx7RdvFxkve5d08ejhfdLA1CCCGEEEIIIYQQQgghhBBCCCGEEEJ3ZRo86Zx8nbYNL2aXb/KVa4cWEEIIIYQQQgghhBBCCCGEEEIIIYQQGlFUbR7Rfwi4dduOjTelgxBCCCGEEEIIIYQQQgghhBBCCCGEEMK9JRnMnu6fvc3Y7GDDBdwQQgghhBBCCCGEEEIIIYQQQgghhBBuGVFsPFXdf/u0tleexdzyfwCEEEIIIYQQQgghhBBCCCGEEEIIIYTvRjj7Xm2b2/bfYm3jiKIWM4QQQgghhBBCCCGEEEIIIYQQQgghhBAeOnXetglF8uR+8jDRNnY69/1ACCGEEEIIIYQQQgghhBBCCCGEEEII4d0Ik6f7k89KnqZPfubVl31DCCGEEEIIIYQQQgghhBBCCCGEEEII4SDCtsua2y4Nbzu0GVFACCGEEEIIIYQQQgghhBBCCCGEEEJ4N8K299p4en3jjdmSBykIIYQQQgghhBBCCCGEEEIIIYQQQgghbEC4cSuEtq11ZwdIyUNk29YnEEIIIYQQQgghhBBCCCGEEEIIIYQQQigJQglCSRBKEEqCUIJQEoQShJIglCCUBKEEoSQIJQglQShBKAlCCUJJEEoQSoJQglAShBKEkiCUIJQg9BVIEEoQSoJQglAShBKEkiCUIJQEoQShJAil1/QD4KOSJVxtgmQAAAAASUVORK5C"
     * }
     *
     */
    @RequestMapping(value = Routes.WECHAT_QRCODE, method = RequestMethod.POST)
    public JsonResult getQRCode(@RequestBody WechatRechargeDto wechatRechargeDto, HttpServletRequest request, HttpServletResponse response) {
        RestDoing doing = jsonResult -> {
            String codeUrl = wechatRechargeDto.getCodeUrl();
            if (StringUtils.isEmpty(codeUrl)) {
                jsonResult.remind("二维码链接不能为空",log);
                return;
            }
            jsonResult.data = qrCodeHelper.createPaymentBase64QRCode(codeUrl,300,300);
        };
        return doing.go(request, log);
    }

    /**
     * 微信支付回调通知
     *
     * 微信回调的数据
     * WxPayJsSDKCallback[
     *   return_code=SUCCESS
     *   appid=wxce62f97f15d07734
     *   mch_id=1296033001
     *   nonce_str=1490862301288
     *   sign=40FFA8F102EF0894BD75152118C1FF04
     *   result_code=SUCCESS
     *   openid=oU83_sljRuCU76FJ_Yh9Vb_69010
     *   is_subscribe=Y
     *   trade_type=NATIVE
     *   bank_type=CFT
     *   total_fee=1
     *   fee_type=CNY
     *   cash_fee=1
     *   transaction_id=4008832001201703305184330434
     *   out_trade_no=2017033016250099000
     *   attach={"payId":2,"checkCode":"37a2a2eb5530dd97ba87a5b5988f137f"}
     *   time_end=20170330162823
     * ]
     * 成功返回数据: <xml>
     *              <return_code><![CDATA[SUCCESS]]></return_code>
     *              <return_msg><![CDATA[OK]]></return_msg>
     *          </xml>
     *
     */
    @Transactional
    @RequestMapping(value = Routes.WECHAT_NOTIFY, method = RequestMethod.POST)
    @ResponseBody
    public void notify(HttpServletRequest request, HttpServletResponse response) {
        WxPayJsSDKCallback callback = null;
        StringBuffer inputData = new StringBuffer();
        try {
            List<String> strings = IOUtils.readLines(request.getInputStream());
            strings.forEach(s -> inputData.append(s));
//            log.info("======================>notify inputData : {}", inputData);
            XStream xstream = XStreamInitializer.getInstance();
            xstream.alias("xml", WxPayJsSDKCallback.class);
            WxPayJsSDKCallback wxMpCallback = (WxPayJsSDKCallback) xstream.fromXML(inputData.toString());
            if (wxMpCallback == null || StringUtils.isBlank(wxMpCallback.getAttach())) {
                log.warn("======================>微信支付恶意回调请求, data : {}", inputData.toString());
                return;
            }
            WechatPaymentAttachDto wechatPaymentAttach = objectMapper.readValue(wxMpCallback.getAttach(), WechatPaymentAttachDto.class);
//            log.info("======================>notify WxMpPayCallback : {}", objectMapper.writeValueAsString(wxMpCallback));

            WxMpService wxMpService = wechatPaymentHelper.getWxMpService(wechatPaymentAttach.getDomain());
            if(wxMpService==null) {
//                log.error("======================>支付配置数据不存在");
                return;
            }
            callback = wxMpService.getPayService().getJSSDKCallbackData(inputData.toString());
            callback.getTransaction_id();
//            log.info("======================>notify result : {}", callback);
            //验证不通过
            boolean isOk = wxMpService.getPayService().checkJSSDKCallbackDataSignature(wechatPaymentHelper.getPayCallbackDataMap(callback),callback.getSign());
            if(!isOk) {
                return;
            }
            Long payId = wechatPaymentAttach.getPayId();
            String payNo = callback.getOut_trade_no();
            String transactionId = callback.getTransaction_id();
            BigDecimal rechargeAmount = new BigDecimal(callback.getTotal_fee());
            String timeEnd = callback.getTime_end();
            String resXml = "";
            // 支付成功 回调
            if ("SUCCESS".equals(callback.getResult_code())) {
                // 当处理成功后,给微信返回成功结果
                wechatRechargeService.createMerchantRechargeFlow(payId,payNo,transactionId,rechargeAmount,timeEnd);
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            } else {
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[ERROR]]></return_msg>" + "</xml> ";
                log.warn("======================>notify {} ", callback.toString());
            }
            response.getWriter().print(resXml);
        } catch (Exception e) {
            // 当如果发生异常数据回滚
            try {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            } catch (NoTransactionException e1) {
            }
            log.warn("======================>notify error {} ", callback.toString());
            super.showErrorLog(e, log);
        }
    }

    /**
     * @api {post} /wechat/recharge/query 微信扫码支付查询
     * @apiGroup wechat
     * @apiVersion 1.0.0
     * @apiParam {String} outTradeNo <code>必须参数</code> 充值号
     *
     * @apiSuccess {String} returnCode 返回状态码 SUCCESS表示成功
     * @apiSuccess {String} returnMsg 返回信息 OK表示成功
     * @apiSuccess {String} tradeState 支付状态 SUCCESS表示成功
     *
     * @apiSuccessExample {json} Success-Response:
     *
     * {
     *   "code": "1",
     *   "desc": "成功",
     *   "data": {
     *     "returnCode": "SUCCESS",
     *     "returnMsg": "OK",
     *     "resultCode": "SUCCESS",
     *     "appid": "wxce62f97f15d07734",
     *     "mchId": "1296033001",
     *     "nonceStr": "OrkMAw4qiiHdnTEC",
     *     "sign": "BE061D16CB2646B9F822D85434988C01",
     *     "openid": "oU83_sljRuCU76FJ_Yh9Vb_69010",
     *     "isSubscribe": "Y",
     *     "tradeType": "NATIVE",
     *     "tradeState": "SUCCESS",
     *     "bankType": "CFT",
     *     "totalFee": 1,
     *     "feeType": "CNY",
     *     "cashFee": 1,
     *     "transactionId": "4008832001201703305184330434",
     *     "outTradeNo": "2017033016250099000",
     *     "attach": "{\"payId\":2,\"checkCode\":\"37a2a2eb5530dd97ba87a5b5988f137f\"}",
     *     "timeEnd": "20170330162823"
     *   }
     * }
     *
     */
    @RequestMapping(value = Routes.WECHAT_RECHARGE_QUERY, method = RequestMethod.POST)
    public JsonResult query(@RequestBody WechatRechargeDto wechatRechargeDto, HttpServletRequest request) {
        RestDoing doing = jsonResult -> {
            String outTradeNo = wechatRechargeDto.getOutTradeNo();
            if (StringUtils.isEmpty(outTradeNo)) {
                jsonResult.remind("充值号不能为空",log);
                return;
            }
            MerchantRechargePayment merchantRechargePayment = wechatRechargeService.findByNum(outTradeNo);
            if(merchantRechargePayment==null) {
                jsonResult.remind("充值号不存在",log);
                return;
            }
            Mall mall = mallRepository.findById(merchantRechargePayment.getMallId());
            if(mall==null) {
                jsonResult.remind("平台不存在",log);
                return;
            }
            WxMpService wxMpService = wechatPaymentHelper.getWxMpService(mall.getMobileHostName());
            if(wxMpService==null) {
                jsonResult.remind("支付配置不存在",log);
                return;
            }
            jsonResult.data = wxMpService.getPayService().queryOrder("",outTradeNo);
        };
        return doing.go(request, log);
    }

}
